{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TVLA Testing for Crypto Validation\n",
    "\n",
    "Supported setups:\n",
    "\n",
    "SCOPES:\n",
    "\n",
    "* OPENADC\n",
    "* CWNANO\n",
    "\n",
    "PLATFORMS:\n",
    "\n",
    "* CWLITEARM\n",
    "* CWLITEXMEGA\n",
    "* CWNANO"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial will perform a basic TVLA test. Here we are only using an unprotected software implementation, so there is little hope of passing the test! But this can demonstrate how the TVLA test can be useful for validating your crypto implementation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "CRYPTO_TARGET = 'TINYAES128C'\n",
    "num_traces = 50\n",
    "CHECK_CORR = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../hardware/victims/firmware/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Capturing Power Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll use some helper scripts to make setup and programming easier. If you're using an XMEGA or STM (CWLITEARM) target, binaries with the correct should be setup for you:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = \"../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex\".format(PLATFORM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition, before we capture our traces, we'll need to create a ChipWhipserer project, since that's what Analyzer expects for an input:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "project = cw.create_project(\"projects/jupyter_tvla_sw.cwp\", overwrite=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Capturing Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below you can see the capture loop. The main body of the loop loads some new plaintext, arms the scope, sends the key and plaintext, then finally records and our new trace into our trace class. We'll also keep track of our keys manually for checking our answer later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Capture Traces\n",
    "import chipwhisperer as cw\n",
    "from tqdm import tnrange\n",
    "import numpy as np\n",
    "import time\n",
    "\n",
    "N = 50  # Number of traces\n",
    "\n",
    "ktp = cw.ktp.TVLATTest()\n",
    "ktp.init(N)\n",
    "\n",
    "keys = []\n",
    "for i in tnrange(N, desc='Capturing traces'):\n",
    "    key, text = ktp.next()  # TVLA T-Test changes PT between two options\n",
    "    keys.append(key)\n",
    "    trace = cw.capture_trace(scope, target, text, key)\n",
    "    if trace is None:\n",
    "        continue\n",
    "    project.traces.append(trace)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have our traces, we need to tell the project that the traces are loaded and add them to the project's trace manager."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Save project file\n",
    "project.save()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're now done with the ChipWhisperer hardware, so we should disconnect from the scope and target:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup the connection to the target and scope\n",
    "scope.dis()\n",
    "target.dis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alright! The following chunk of code does the entire TVLA algorithm itself, based on the recorded data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews as hv\n",
    "hv.extension('bokeh')\n",
    "import numpy as np\n",
    "import scipy\n",
    "import scipy.stats\n",
    "\n",
    "project = cw.open_project('projects/jupyter_tvla_sw.cwp')\n",
    "fixedpy = [0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90]\n",
    "\n",
    "testouts = []\n",
    "num_traces = len(project.traces)\n",
    "num_points = len(project.waves[0])\n",
    "print(num_traces, num_points)\n",
    "curve = hv.Curve([])\n",
    "\n",
    "def do_the_ttvla(project, ntraces=-1):\n",
    "    global curve, line\n",
    "    if ntraces == -1:\n",
    "        ntraces = int(num_traces / 2)\n",
    "        \n",
    "    if ntraces * 2 > num_traces:\n",
    "        raise ValueError(\"Invalid ntraces\")\n",
    "    for g in range(0, 2):\n",
    "        group = [(list(project.textins[i]) == fixedpy) for i in range(g*ntraces, g*ntraces+ntraces)]\n",
    "        trace = np.zeros((ntraces, num_points))\n",
    "\n",
    "        for n in range(g*ntraces, g*ntraces+ntraces):\n",
    "            trace[n - g*ntraces][:] = project.waves[n]\n",
    "                \n",
    "        testout = welch_ttest(group, trace)\n",
    "        curve *= hv.Curve(testout)\n",
    "        testouts.extend(testout)\n",
    "        \n",
    "    curve *= hv.Path([(0, -4.5), (num_points, -4.5)]).opts(color=\"black\") * \\\n",
    "    hv.Path([(0, 4.5), (num_points, 4.5)]).opts(color=\"black\")\n",
    "\n",
    "def welch_ttest(group, traces):\n",
    "    import warnings\n",
    "    # Compute Welch's t-statistic at each point in time\n",
    "    # Here, group[] must only contain booleans (True/False)\n",
    "    traces_true = traces[np.where(np.array(group))]\n",
    "    traces_false = traces[np.where(~np.array(group))]\n",
    "    \n",
    "    if len(traces_true) == 0:\n",
    "        traces_true  = np.array([[np.nan for _ in range(len(traces[0]))]])\n",
    "    if len(traces_false) == 0:\n",
    "        traces_false = np.array([[np.nan for _ in range(len(traces[0]))]])\n",
    "    \n",
    "    with warnings.catch_warnings():\n",
    "        warnings.simplefilter(\"ignore\")\n",
    "        ttrace = scipy.stats.ttest_ind(traces_true, traces_false, axis=0, equal_var=False)[0]\n",
    "        \n",
    "    return np.nan_to_num(ttrace) \n",
    "    \n",
    "do_the_ttvla(project)\n",
    "(curve).opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With multiple excursions beyond the 4.5 limit, you can see that the implementation easily fails! The only thing we haven't done right here is figure out *where* the actual crypto stards and ends. This is best done using a T-Test or similar targetting the input and output data of the crypto operation. In this example we didn't even cover the complete portion of the algorithm, but this is easier to do with hardware crypto."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tests"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_leakage = 0\n",
    "for point in np.abs(testouts):\n",
    "    if point > max_leakage:\n",
    "        max_leakage = point\n",
    "assert max_leakage > 4.5, \"Device passed test! Max leakage = {}\".format(max_leakage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
